/*================================================================
*   Copyright (C) 2024 baichao All rights reserved.
*
*   文件名称：zombie_process.c
*   创 建 者：baichao
*   创建日期：2024年03月31日
*   描    述：例子中父进程休眠300秒后才会调用wait来获取子进程的退出信息。
*            而子进程退出之后会变成僵尸状态，苦苦等待父进程来获取退出信息。
*            在这300秒左右的时间里，子进程就是一个僵尸进程。
*
================================================================*/

/*
 * 如何查看一个进程是否处于僵尸状态呢？
 *  ps命令输出的进程状态Z，就表示进程处于僵尸状态;
 *  另外procfs提供的status信息中的State给出的值是Z（zombie），也表明进程处于僵尸状态。
 * 
 * 进程一旦进入僵尸状态，就进入了一种刀枪不入的状态，“杀人不眨眼”的kill-9也无能为力，因为谁也没有办法杀死一个已经死去的进程。
 * 清除僵尸进程有以下两种方法：
 *   ·父进程调用wait函数，为子进程“收尸”。
 *   ·父进程退出，init进程会为子进程“收尸”。
 * 一般而言，系统不希望大量进程长期处于僵尸状态，因为会浪费系统资源。除了少量的内存资源外，比较重要的是进程ID。
 * 僵尸进程并没有将自己的进程ID归还给系统，而是依然占有这个进程ID，因此系统不能将该ID分配给其他进程。
 * 
 * 对于编程来说，如何防范僵尸进程的产生呢？答案是具体情况具体分析。
 *   ·如果我们不关心子进程的退出状态，就应该将父进程对SIGCHLD的处理函数设置为SIG_IGN，或者在调用sigaction函数时设置SA_NOCLDWAIT标志位。这两者都会明确告诉子进程，父进程很“绝情”，不会为子进程“收尸”。子进程退出的时候，内核会检查父进程的SIGCHLD信号处理结构体是否设置了SA_NOCLDWAIT标志位，或者是否将信号处理函数显式地设为SIG_IGN。如果是，则autoreap为true，子进程发现autoreap为true也就“死心”了，不会进入僵尸状态，而是调用release_task函数“自行了断”了。
 *   ·如果父进程关心子进程的退出信息，则应该在流程上妥善设计，能够及时地调用wait，使子进程处于僵尸状态的时间不会太久。
 * 对于创建了很多子进程的应用来说，知道子进程的返回值是有意义的。
 * 比如说父进程维护一个进程池，通过进程池里的子进程来提供服务。当子进程退出的时候，父进程需要了解子进程的返回值来确定子进程的“死因”，从而采取更有针对性的措施。 
 */
#if 0

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
  pid_t pid;
  pid = fork();
  if (pid < 0) {
    /* 如果出错 */
    printf("error occurred!\n");
  } else if (pid == 0) {
    /* 子进程 */
    exit(0);
  } else {
    /* 父进程 */
    sleep(300); /* 休眠300秒 */
    wait(NULL); /* 获取僵尸进程的退出信息 */
  }
  return 0;
}

#elif 1

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

int main() {
  pid_t pid;
  struct sigaction sa;

  // 设置信号处理函数为SIG_IGN，忽略SIGCHLD信号
  sa.sa_handler = SIG_IGN;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGCHLD, &sa, NULL);

  pid = fork();
  if (pid < 0) {
    /* 如果出错 */
    printf("error occurred!\n");
  } else if (pid == 0) {
    /* 子进程 */
    exit(0);
  } else {
    /* 父进程 */
    sleep(300); /* 休眠300秒 */
    // 父进程不再调用wait函数来获取子进程的退出信息
  }
  return 0;
}

#endif
